package cn.snmp.mutual.common;

import java.io.IOException;

import cn.snmp.mutual.common.snmp.SnmpNioTarget;
import cn.snmp.mutual.common.snmp.SnmpNioTargetUtilities;
import cn.snmp.mutual.constants.NnTempLetError;
import cn.snmp.mutual.nms.ErrorMessages;
import cn.snmp.mutual.nms.SnmpOID;
import cn.snmp.mutual.nms.SnmpVar;
import cn.snmp.mutual.nms.SnmpVarBind;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.MessageException;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import cn.snmp.mutual.data.ObjService;
import cn.snmp.mutual.handler.SnmpGetBulk;
import cn.snmp.mutual.handler.SnmpGetList;
import cn.snmp.mutual.handler.SnmpGetTable;
import cn.snmp.mutual.handler.SnmpGetTablePage;
import cn.snmp.mutual.handler.SnmpGetTableRow;
import cn.snmp.mutual.util.OperatorUtilities;

/**
 * @author dengkn
 * SNMP 发送流程处理，同步消息监听
 * 还没有实现异步消息，异步消息处理比较麻烦，
 * 客户端对接业务比较麻烦，配置下发和获取大多没有异步需求
 */
@Slf4j
public enum SnmpDispatcher {

    snmpDispatcher, snmpDispatcherIpv6("IPV6");

    {
        USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
        SecurityModels.getInstance().addSecurityModel(usm);
    }

    int countOperator = 0;
    private Snmp snmp = null;
    private boolean isMaxPage = false;
    private int MaxPage = 2;

    SnmpDispatcher() {
        try {
            //UdpTransportMapping udpmap = new UdpTransportMapping()
            DefaultUdpTransportMapping udpmap = new DefaultUdpTransportMapping();
            snmp = new Snmp(udpmap);
            udpmap.listen();
        } catch (IOException e) {

        }
    }

    SnmpDispatcher(String ipType) {
        try {
            DefaultUdpTransportMapping udpmap = new DefaultUdpTransportMapping();
            snmp = new Snmp(udpmap);
            udpmap.listen();
        } catch (IOException e) {
        }
    }

    public Snmp getSnmp() {
        return snmp;
    }

    /**
     * 发送SNMP请求数据
     *
     * @param nioTarget 发送数据的实例
     * @throws Exception
     */
    public void sendPdu(SnmpNioTarget nioTarget) throws MessageException {
        sendPduOperator(nioTarget);
        while (isMaxPage) {
            sendPduOperator(nioTarget);
        }
    }

    public void sendPduOperator(SnmpNioTarget nioTarget) throws MessageException {
        Target target = nioTarget.getTarget(nioTarget);
        PDU pdu = nioTarget.getPDU();
        pdu.setMaxRepetitions(1000);
        if (target.getVersion() == SnmpConstants.version3) {
            snmp.getUSM().addUser(nioTarget.getUsmUser());
        }
	/*	logger.info("mutual send pud ErrorStatus:"+ pdu.getErrorStatus());
		logger.info("mutual send target:"+ target.toString());
		for(int i = 0;i<pdu.getVariableBindings().size(); i++) {
			logger.info("send pud mib : " + pdu.getVariableBindings().get(i));
		}*/
        if (pdu.getRequestID() == null) {
            log.error("mutual send pud request id: NULL");
        }

        try {

            ResponseEvent response;
            response = snmp.send(pdu, target);
            // Thread.sleep(20000);

//			log.info("Trap old send device...");
//			log.info("Wait device response...");
            if (response != null) {
                PDU pud = response.getResponse();
                log.info("mutual response pud request id:" + pdu.getRequestID());
                log.info("response message from :  " + response.getPeerAddress());
                log.info("response ErrorIndex: " + response.getResponse().getErrorIndex());
                log.info("response ErrorIndex: " + response.getResponse().getErrorStatus());
                for (int i = 0; i < response.getResponse().getVariableBindings().size(); i++) {
                    log.info("response : " + response.getResponse().getVariableBindings().get(i));
                }
                if (pud != null) {
                    countOperator++;
                    asynHandleResponse(pud, nioTarget);
                }
            } else {
                log.error("mutual response is null, not found device trap!");
            }

        } catch (Exception e) {
            initResultError(nioTarget);
            log.error("mutual send exception, error", e);
        }

    }

    private void initResultError(SnmpNioTarget nioTarget) {
        ObjService result = nioTarget.getResult();
        result.setValue("ErrCode", "-2");
        result.setValue("ErrDesc", "operator error!");
    }

    private void asynHandleResponse(PDU response, SnmpNioTarget target) {
        if (response == null) {
            initResultError(target);
            parser(target);
            log.error("mutual response pud is null, error operator!");
        } else {
            VariableBinding[] vb = new VariableBinding[response.getVariableBindings().size()];
            response.getVariableBindings().toArray(vb);
            SnmpVarBind[] snmpVarBinds = new SnmpVarBind[vb.length];
            for (int i = 0; i < vb.length; i++) {
                VariableBinding recVB = vb[i];
                SnmpVar var = SnmpNioTargetUtilities.changeVariable(recVB);
                SnmpOID oid = new SnmpOID(recVB.getOid().getValue());
                snmpVarBinds[i] = new SnmpVarBind(oid, var);
            }

            if (countOperator >= MaxPage && target.hasNextPDU()) {
                isMaxPage = true;
                MaxPage = MaxPage + 1000;
            } else {
                isMaxPage = false;
            }
            log.debug(response.toString());
            target.operateOrgResult(snmpVarBinds, response);
            if (isMaxPage) {
                return;
            }
            if (target.hasNextPDU()) {
                try {
                    sendPduOperator(target);
                } catch (Exception e) {
                    isMaxPage = false;
                    log.error("mutual requst data = null! ", e);
                    target.setErrCode(NnTempLetError.IO_ERROR);
                    target.setErrDes(e.getMessage());
                    countOperator = 0;
                    target.sethasNext(false);
                    parser(target);
                }
            } else {
                target.sethasNext(false);
                parser(target);
            }
        }
    }

    /**
     * 对设备的返回结果进行解析、格式化操作
     *
     * @param target 返回结果
     * @return ObjService 最终格式化后的结果
     */
    public SnmpNioTarget parser(SnmpNioTarget target) {
//		log.debug("Interact with the device and start assembling results:");
        ObjService result = target.getResult();
        dataTypeHandler(target, result);
        if (target.getSnmpOpType() != SnmpNioTarget.SNMP_SET_INFO) {
            ObjService osCommandParams = target.getOsParam();
            String strValueOnly = osCommandParams.getStringValue("ValueOnly");
            if (strValueOnly.equals("")) {
                strValueOnly = result.getStringValue("ValueOnly");
            }
            OperatorUtilities.setTempletVersion(result);
            if (strValueOnly.equalsIgnoreCase("true")) {
                ObjService osValueResult = new ObjService();
                osValueResult.setClsName(result.getClsName());
                osValueResult.setCurrentHashMap(result.getStoreValueMap());
                for (int i = 0; i < result.objectSize("RowSet"); i++) {
                    osValueResult.addContainedObject(result.objectAt("RowSet", i));
                }
                result = osValueResult;
            }
        }
        return target;
    }

    private void dataTypeHandler(SnmpNioTarget target, ObjService result) {
        if (target.getErrorCode() != 0) {
            result.setValue("isFinish", "true");
            if (target.getSnmpOpType() != SnmpNioTarget.SNMP_SET_INFO && target.getErrorCode() == 1) {
                result.setValue("ErrCode", "0");
                result.setValue("ErrDesc", target.getErrorString());
            } else {
                result.setValue("ErrCode", "" + target.getErrorCode());
                result.setValue("ErrDesc", target.getErrorString());
            }
            if (target.getSnmpOpType() == SnmpNioTarget.SNMP_SET_INFO) {
                SnmpSetPread.parser4Error(target);
            }

        } else {
            switch (target.getSnmpOpType()) {
                case SnmpNioTarget.SNMP_GET_LIST:
                    SnmpGetList.parser(target);
                    break;
                case SnmpNioTarget.SNMP_GET_TABLE:
                    SnmpGetTable.parser(target);
                    break;
                case SnmpNioTarget.SNMP_GET_TABLE_PAGE:
                    SnmpGetTablePage.parser(target);
                    break;
                case SnmpNioTarget.SNMP_GET_TABLE_ROW:
                    SnmpGetTableRow.parser(target);
                    break;
                case SnmpNioTarget.SNMP_SET_INFO:
                    SnmpSetPread.parser(target);
                    break;
                case SnmpNioTarget.SNMP_GET_BULK:
                    SnmpGetBulk.parser(target);
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 异步获取实现，这里占时没有用，保留
     */
    @Slf4j
    class AsynResponse implements ResponseListener {
        @Override
        public void onResponse(ResponseEvent event) {
            ((Snmp) event.getSource()).cancel(event.getRequest(), this);
            PDU response = event.getResponse();
            SnmpNioTarget target = (SnmpNioTarget) event.getUserObject();
            if (response == null) {
                target.setErrCode(ErrorMessages.REQ_TIMEDOUT);
                target.setErrDes("Snmp Request Timed Out." + target.getAddress());
                log.error("Snmp get response data = null");
            } else {
                asynHandleResponse(response, target);
            }
        }

        private void asynHandleResponse(PDU response, SnmpNioTarget target) {
            VariableBinding[] vb = new VariableBinding[response.getVariableBindings().size()];
            response.getVariableBindings().toArray(vb);
            SnmpVarBind[] snmpVarBinds = new SnmpVarBind[vb.length];
            for (int i = 0; i < vb.length; i++) {
                VariableBinding recVB = vb[i];
                SnmpVar var = SnmpNioTargetUtilities.changeVariable(recVB);
                SnmpOID oid = new SnmpOID(recVB.getOid().getValue());
                snmpVarBinds[i] = new SnmpVarBind(oid, var);
            }
            target.operateOrgResult(snmpVarBinds, response);
            if (target.hasNextPDU()) {
                try {
                    sendPdu(target);
                } catch (Exception e) {
                    e.printStackTrace();
                    target.setErrCode(NnTempLetError.IO_ERROR);
                    target.setErrDes(e.getMessage());
                    parser(target);
                }
            } else {
                parser(target);
                log.debug("NetNodeID = " + target.getNEID() + " TableName = " + target.getOsParam().getStringValue("TableName") + " Get End.");
            }
        }
    }

    private void getHandle(ResponseEvent event) {
        PDU response = event.getResponse();
        SnmpNioTarget target = (SnmpNioTarget) event.getUserObject();
        if (response == null) {
            target.setErrCode(ErrorMessages.REQ_TIMEDOUT);
            target.setErrDes("Snmp Request Timed Out." + target.getAddress());
        } else {
            asynHandleResponse(response, target);
        }
    }
}
